package top.rainbowecho.gateway.controller;

import cn.hutool.core.util.RandomUtil;
import com.google.common.collect.ImmutableMap;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.savedrequest.SavedRequest;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import top.rainbowecho.common.exception.BlogException;
import top.rainbowecho.common.mq.config.BlogMqRouteEnum;
import top.rainbowecho.common.mq.config.MqConfiguration;
import top.rainbowecho.common.mq.mo.MailMeta;
import top.rainbowecho.common.util.CommonUtil;
import top.rainbowecho.common.util.ExceptionMessageContent;
import top.rainbowecho.common.util.MailKey;
import top.rainbowecho.common.util.ResultKey;
import top.rainbowecho.gateway.security.authentication.mail.ValidateCode;
import top.rainbowecho.gateway.security.property.SecurityProperties;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author rainbow
 * @since 2019/12/25 17:42
 */
@RestController
@Slf4j
@Api("认证相关Api")
public class GatewayController {

    /**
     * 用于取出认证后要跳转的URL
     */
    private RequestCache requestCache = new HttpSessionRequestCache();

    private StringRedisTemplate redisTemplate;

    private RabbitTemplate rabbitTemplate;

    private MqConfiguration mqConfiguration;

    private SecurityProperties securityProperties;

    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    @Autowired
    public void setSecurityProperties(SecurityProperties securityProperties) {
        this.securityProperties = securityProperties;
    }

    @Autowired
    public void setMqConfiguration(MqConfiguration mqConfiguration) {
        this.mqConfiguration = mqConfiguration;
    }

    @Autowired
    public void setRabbitTemplate(RabbitTemplate rabbitTemplate) {
        this.rabbitTemplate = rabbitTemplate;
    }

    @Autowired
    public void setRedisTemplate(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @ApiOperation("表单登录页面")
    @GetMapping("/login")
    public ResponseEntity<Map<String, Object>> authentication(HttpServletRequest request, HttpServletResponse response) throws IOException {
        SavedRequest savedRequest = requestCache.getRequest(request, response);

        if (savedRequest != null) {
            String targetUrl = savedRequest.getRedirectUrl();
            log.info("引发认证的URL请求是：" + targetUrl);
            boolean isHtmlRequest = StringUtils.endsWithIgnoreCase(targetUrl, ".html");
            if (isHtmlRequest) {
                redirectStrategy.sendRedirect(request, response, "/login.html");
            }
        }
        ImmutableMap<String, Object> params = ImmutableMap.of(ResultKey.MESS, ExceptionMessageContent.NO_AUTHENTICATION);
        return new ResponseEntity<>(params, HttpStatus.UNAUTHORIZED);
    }

    @PostMapping("/code")
    @ApiOperation("根据邮件或电话号码生成登录验证码，用于登录")
    public ResponseEntity<Map<String, Object>> mailValidate(@RequestParam("contact") String contact) {
        // 如果不是邮件格式或电话号码格式，则抛出异常
        boolean isMail = CommonUtil.matchEmail(contact);
        boolean isPhone = CommonUtil.matchMobilePhone(contact);
        if (!isMail && !isPhone) {
            throw new BlogException(ExceptionMessageContent.INVALID_PARAM);
        }

        long timestamp = Instant.now().getEpochSecond();

        // redis中的验证码键
        String codeKey = CommonUtil.validateCodeKey(contact, timestamp);

        int codeLength = this.securityProperties.getCode().getLength();
        Duration timeout = this.securityProperties.getCode().getTtl();
        long ttl = timeout.getSeconds();

        // 生成随机验证码
        ValidateCode codeValue = generateCode(codeLength, ttl);

        ArrayList<String> to = new ArrayList<>();
        to.add(contact);

        ImmutableMap<String, Object> mailParam = null;
        if (isMail) {
            // 使用mq发送邮箱消息
            mailParam = ImmutableMap
                    .of(MailKey.CODE, codeValue.getCode(), MailKey.TIMEOUT, timeout.toMinutes());
        }

        if (isPhone) {
            // 使用mq发送登录短信
        }

        MailMeta mailMeta = new MailMeta(to, MailKey.MAIL_LOGIN_TEMPLATE, mailParam);
        String routing = mqConfiguration.getRoutes().get(BlogMqRouteEnum.GATEWAY_TO_MAIL).getRouting();
        rabbitTemplate.convertAndSend(routing, mailMeta);

        // 存入验证码到redis
        redisTemplate.opsForValue().set(codeKey, codeValue.getCode(), codeValue.getExpireTime(), TimeUnit.SECONDS);

        ImmutableMap<String, Object> params = ImmutableMap
                .of(ResultKey.MAIL, contact, ResultKey.TIMESTAMP, timestamp, ResultKey.EXPIRE, ttl);

        return new ResponseEntity<>(params, HttpStatus.OK);
    }

    private ValidateCode generateCode(int length, long ttl) {
        String code = RandomUtil.randomString(length);

        return new ValidateCode(code, ttl);
    }
}
